#include "MainWindow.h"
#include "serialportlistenthread.h"
#include <QLineEdit>
#include <QTextBrowser>
#include <QLabel>
#include <QFrame>
#include <QStatusBar>
#include <QMenuBar>
#include <QThread>
#include <QDebug>
#include <QButtonGroup>
#include <QRadioButton>
#include <QRegularExpression>

using namespace  std;

#define DEAFULT_WINDOW_SIZE_X  800
#define DEAFULT_WINDOW_SIZE_Y  640

#define MIN_WINDOW_SIZE_X  (640)
#define MIN_WINDOW_SIZE_Y  (480)

/*
 * style sheet:
 *
 * "padding: 1px 18px 1px 3px;"
 *
 * */

#define H_GRID_LEN  10
#define L_FRID_LEN  15


#define StyleSheet_QComboBox    "background-color: yellow;" \
                                "border-width: 2px solid gray;" \
                                "border-radius: 5px;"

#define StyleSheet_QGroupBox    "background-color: red;" \
                                "border: 1px solid gray;" \
                                "border-radius: 5px;" \
                                "margin-top: 1ex;"

#define StyleSheet_QLabel       "background-color: transparent;" \
                                "border:none"


#define SETTING_PANEL_MAX_W     230

/**
 * @brief a hex checker 
 */
#define HexPattern "^(((\\d|[a-f])([0-9]|[a-f]) )|((\\d|[a-f])(\\d|[a-f])))*"
const QRegularExpression hexmatcher(HexPattern);


MainWindow::MainWindow(QMainWindow *parent)
    : QMainWindow(parent)
{
    s_conf = new SerialConfig_t;

    /*
     * serial init
     * */
    serial = new SerialManager;
    connect(this, &MainWindow::SingleTx, serial, &SerialManager::SingleTx);

    /* init cycel send timer signal and slot */
    send_tm = new QTimer(this);
    connect(send_tm, &QTimer::timeout, serial, &SerialManager::CycelTx);

    SerialPortListenThread* listenthread = new SerialPortListenThread;
    connect(listenthread, &SerialPortListenThread::Refresh,
            this, &MainWindow::SerialBoxRefresh);
    listenthread->start();


    /**
     * init status bar
     */
    QStatusBar* statusbar = new QStatusBar(w);
    setStatusBar(statusbar);
    statusbar->showMessage(tr("Ready"));

    /* main window init */
    setCentralWidget(w);
    MainWindow::setMinimumSize(MIN_WINDOW_SIZE_X, MIN_WINDOW_SIZE_Y);
    MainWindow::resize(DEAFULT_WINDOW_SIZE_X, DEAFULT_WINDOW_SIZE_Y);


    w->setStyleSheet("{ QComboBox " StyleSheet_QComboBox "}");
    w->setStyleSheet("{ QGroupBox " StyleSheet_QGroupBox "}");
    w->setStyleSheet("{ QLabel " StyleSheet_QLabel "}");
    
    QGridLayout* toplayout = new QGridLayout(w);

    QVBoxLayout* settinglayout = new QVBoxLayout();
    settinglayout->addWidget(CreateSerialSetting());
    settinglayout->addWidget(CreateSendSetting());
    settinglayout->addWidget(CreateRecvSetting());

    QGridLayout* txrxlayout = CreateTxRxLayout();

    toplayout->addLayout(txrxlayout, 0, 0, 1, 4);
    toplayout->addLayout(settinglayout, 0, 5, 1, 1);

    w->setLayout(toplayout);
}

MainWindow::~MainWindow()
{
    delete w;
}

/** ***************************************************************************
 * @brief   刷新串口列表，被 SerialPortListenThread.Refresh() 触发
 */
void MainWindow::SerialBoxRefresh(const QStringList& list)
{
    m_serialportbox->clear();
    for (int i = 0; i < list.size(); i++) {
        m_serialportbox->addItem(list[i]);
        qDebug() << "dtk" << list[i];
    }
}

/** ***************************************************************************
 * @brief   创建串口设置组件
 */
QGroupBox* MainWindow::CreateSerialSetting()
{
    QGroupBox *serialsettinggroup = new QGroupBox(tr("串口设置"));

    serialsettinggroup->setStyleSheet("{ QComboBox " StyleSheet_QComboBox "}");
    serialsettinggroup->setStyleSheet("{ QGroupBox " StyleSheet_QGroupBox "}");
    serialsettinggroup->setStyleSheet("{ QLabel " StyleSheet_QLabel "}");

    serialsettinggroup->setStyleSheet("{ QGroupBox " StyleSheet_QGroupBox "}");
    QLabel* serialselect = new QLabel("串口: ");
    m_serialportbox = new QComboBox();

    QLabel* bdselectlable = new QLabel("波特率: ");
    QLineEdit* bdselectedit = new QLineEdit("115200");

    QLabel* databitlable = new QLabel("数据位: ");
    QComboBox* databitbox = new QComboBox();
    databitbox->addItem("8");

    QLabel* paritybitlable = new QLabel("校验位: ");
    QComboBox* paritybitbox = new QComboBox();
    paritybitbox->addItem("NONE");

    QLabel* stopbitlable = new QLabel("停止位: ");
    QComboBox* stopbitbox = new QComboBox();
    stopbitbox->addItem("1");

    QPushButton* openclose = new QPushButton("打开");

    QGridLayout *groupBoxlayout = new QGridLayout();
    groupBoxlayout->setMargin(10);
    groupBoxlayout->addWidget(serialselect, 0, 0);
    groupBoxlayout->addWidget(m_serialportbox, 0, 1);
    groupBoxlayout->addWidget(bdselectlable, 1, 0);
    groupBoxlayout->addWidget(bdselectedit, 1, 1);

    groupBoxlayout->addWidget(databitlable, 2, 0);
    groupBoxlayout->addWidget(databitbox, 2, 1);
    groupBoxlayout->addWidget(paritybitlable, 3, 0);
    groupBoxlayout->addWidget(paritybitbox, 3, 1);
    groupBoxlayout->addWidget(stopbitlable, 4, 0);
    groupBoxlayout->addWidget(stopbitbox, 4, 1);
    
    groupBoxlayout->addWidget(openclose, 5, 0, 1, 2);

    groupBoxlayout->setColumnStretch(0, 2);
    groupBoxlayout->setColumnStretch(1, 3);

    serialsettinggroup->setLayout(groupBoxlayout);
    serialsettinggroup->setMaximumWidth(SETTING_PANEL_MAX_W);


    /*
     * open close
     */
    connect(openclose, &QPushButton::clicked, this, [ = ]{

        if (openclose->text() == "关闭") {
            openclose->setText("打开");
            statusBar()->showMessage("串口连接已断开");
            serial->Instance()->close();
            return;
        }

        QString portname = m_serialportbox->currentText();
        int bd = bdselectedit->text().toInt();
        serial->Instance()->setPortName(portname);
        serial->Instance()->setBaudRate(bd);

        int ret = serial->Instance()->open(QIODevice::ReadWrite);
        if (ret) {
            QSerialPortInfo SerialInfo(portname);
            statusBar()->showMessage("串口已连接");
            openclose->setText("关闭");
            return;
        }
        serial->Instance()->error();
        QString msg = "无法连接至串口";
        statusBar()->showMessage(msg);
    });

    return serialsettinggroup;
}

/** ***************************************************************************
 * @brief   创建发送设置组件
 */
QGroupBox* MainWindow::CreateSendSetting()
{
    QGroupBox* sendgroup = new QGroupBox("发送设置");

    QButtonGroup* txmodegroup = new QButtonGroup();
    QRadioButton* ascii = new QRadioButton("ASCII");
    QRadioButton* hex = new QRadioButton("HEX");
    txmodegroup->addButton(ascii, AsciiMode);
    txmodegroup->addButton(hex, HexMode);
    ascii->setChecked(true);
    s_conf->txmode = AsciiMode;


    QCheckBox* cyclet = new QCheckBox("循环发送");
    QLineEdit* period_edit = new QLineEdit("1000");
    period_edit->setPlaceholderText("1000 (ms)");
    period_edit->setMaxLength(9);
    cycle_send_period = 0;

    QGridLayout* sendsettinglayout = new QGridLayout();
    sendsettinglayout->setMargin(5);
    sendsettinglayout->addWidget(ascii, 0, 0);
    sendsettinglayout->addWidget(hex, 0, 1);
    sendsettinglayout->addWidget(cyclet, 1, 0);
    sendsettinglayout->addWidget(period_edit, 1, 1);
    sendgroup->setLayout(sendsettinglayout);
    sendgroup->setMaximumWidth(SETTING_PANEL_MAX_W);

    connect(txmodegroup, &QButtonGroup::idClicked, this, [ = ](int id) {
        qDebug() << "id :" <<id;
        switch (id)
        {
        case AsciiMode: 
            qDebug() << "txmode:" << "ASCII";
            s_conf->txmode = AsciiMode;
            break;
        case HexMode:
            qDebug() << "txmode:" << "HEX";
            s_conf->txmode = HexMode;
        default:
            break;
        }
        
        return;
    });

    /* widget to set send period and wether enable it */
    connect(cyclet, &QCheckBox::clicked, this, [ = ](bool set) {
        cycle_send_period = period_edit->text().toInt();
        if (set && cycle_send_period > 0) {
        }
        else {
            send_tm->stop();
            cycle_send_period = 0;
        }
        qDebug() << "cycle_send_period: " << cycle_send_period;
    });
    connect(period_edit, &QLineEdit::textChanged, this, [ = ](const QString& str) {
        cycle_send_period = str.toInt();
        if (cycle_send_period) {
            cyclet->setChecked(true);
        }
        else {
            cyclet->setChecked(false);
            send_tm->stop();
        }
        
        qDebug() << "cycle_send_period: " << cycle_send_period;
    });

    return sendgroup;
}


/** ***************************************************************************
 * @brief   创建接收设置组件
 */
QGroupBox* MainWindow::CreateRecvSetting()
{
    QGroupBox* recvgroup = new QGroupBox("接收设置");

    QButtonGroup* rxmodegroup = new QButtonGroup();
    QRadioButton* ascii = new QRadioButton("ASCII");
    QRadioButton* hex = new QRadioButton("HEX");
    rxmodegroup->addButton(ascii, AsciiMode);
    rxmodegroup->addButton(hex, HexMode);
    ascii->setChecked(true);
    s_conf->rxmode = AsciiMode;

    QGridLayout* recvsettinglayout = new QGridLayout();
    recvsettinglayout->setMargin(5);
    recvsettinglayout->addWidget(ascii, 0, 0);
    recvsettinglayout->addWidget(hex, 0, 1);
    recvgroup->setLayout(recvsettinglayout);
    recvgroup->setMaximumWidth(SETTING_PANEL_MAX_W);

    connect(rxmodegroup, &QButtonGroup::idClicked, this, [ = ](int id) {
        qDebug() << "id :" <<id;
        switch (id) {
            case AsciiMode: 
                qDebug() << "rxmode:" << "ASCII";
                s_conf->rxmode = AsciiMode;
                break;
            case HexMode:
                qDebug() << "rxmode:" << "HEX";
                s_conf->rxmode = HexMode;
            default:
                break;
        }
        
        return;
    });

    return recvgroup;
}


/** ***************************************************************************
 * @brief   创建发送输入编辑/控制组件
 */
QGridLayout* MainWindow::CreateTxRxLayout()
{
    QGridLayout* txrxlayout = new QGridLayout;

    m_sendtext = new QTextEdit("1234567|");
    m_sendtext->setStyleSheet("border:1px solid grey;");

    m_sendbtn = new QPushButton("发送");
    QPushButton* recv_clearbtn = new QPushButton("^");
    QPushButton* send_clearbtn = new QPushButton("<");
    QGridLayout* btnlayout = new QGridLayout;
    send_clearbtn->setMaximumSize(40, 40);
    recv_clearbtn->setMaximumSize(40, 40);
    m_sendbtn->setMaximumSize(85, 40);
    btnlayout->addWidget(send_clearbtn, 0, 0);
    btnlayout->addWidget(recv_clearbtn, 0, 1);
    btnlayout->addWidget(m_sendbtn, 1, 0, 1, 2);


    QTextBrowser* recvtext = new QTextBrowser();

    recvtext->setStyleSheet("border: 1px solid gray;");

    m_sendtext->setMaximumHeight(80);
    txrxlayout->addWidget(recvtext, 0, 0, 1, 2);
    txrxlayout->addWidget(m_sendtext, 1, 0);
    txrxlayout->addLayout(btnlayout, 1, 1);
    txrxlayout->setRowStretch(0, 2);


    connect(send_clearbtn, &QPushButton::clicked, m_sendtext, &QTextEdit::clear);
    connect(recv_clearbtn, &QPushButton::clicked, recvtext, &QTextEdit::clear);

    /**
     * when send button clicked signal tiggered
     */
    connect(m_sendbtn, &QPushButton::clicked, this, [ = ] {

        if (!serial->Instance()->isOpen()) {
            statusBar()->showMessage("串口未打开");
            return;
        }

        QString data = m_sendtext->toPlainText();
        QByteArray read_arr = data.toLatin1();
        QByteArray send_arr = read_arr;

        switch (s_conf->txmode) {
            case HexMode : {
                QRegularExpressionMatch match = hexmatcher.match(data);
                if (match.capturedTexts()[0] == data) {
                    send_arr = QByteArray::fromHex(read_arr);
                }
                else {
                    statusBar()->showMessage("错误的数据格式!");
                    return;
                }
                break;
            }
            case AsciiMode : {
                /* do nothing */
                break;
            }
        }

        qDebug() << "sendmsg : " << send_arr;
        if (cycle_send_period) {
            serial->ClearAndAddMessage(send_arr);
            send_tm->start(cycle_send_period);
            return;
        }

        emit SingleTx(send_arr);
    });

    /**
     * when serial receive signal tiggered
     */
    connect(serial->Instance(), &QSerialPort::readyRead, this, [ = ] {
        QByteArray receiveArray = serial->Instance()->readAll();
        qDebug() << "serial receive signal tiggered";
        switch (s_conf->rxmode) {
            case AsciiMode : {
                recvtext->insertPlainText(QString(receiveArray));
                break;
            }
            case HexMode : {
                const QString& raw_str = QString(receiveArray.toHex());
                QString str;
                int ri = 0;
                int i = 0;
                while (ri < raw_str.size()) {
                    str.replace(i, 2, raw_str.mid(ri, 2));
                    i += 2;
                    ri += 2;
                    str.replace(i++, 1, " ");
                }
                recvtext->insertPlainText(str);
                break;
            }
        }
        recvtext->moveCursor(QTextCursor::End);
    });

    return txrxlayout;
}
